package cn.rulian.base.common.interceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import cn.rulian.base.auth.ann.Access;
import cn.rulian.base.auth.service.RoleService;
import cn.rulian.base.common.dto.Message;
import cn.rulian.base.common.dto.UserInfo;
import cn.rulian.base.common.util.xx;

import com.google.gson.Gson;

/**
 * 权限认证的拦截器（含登录认证、权限认证）
 */
@Component
public class AuthInterceptor implements HandlerInterceptor
{

	@Autowired
	private RoleService roleService;

	private static Map<String,String> authMap; //全部权限集合（权限码：角色ID串）
	
	/**
	 * Handler执行之前调用这个方法
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
	{
		
		String url = request.getRequestURI(); //请求URL
		
		//排除例外
		if(url.endsWith("/") || url.endsWith("/login")|| url.endsWith("/noAccess")|| url.endsWith("/noSession") || url.endsWith("/kc/bill/save"))
		{//除去登录界面 和 登录认证。(外加单据传送地址)
			return true;
		}
		
		//登录验证
		HttpSession session = request.getSession();
		UserInfo userInfo = (UserInfo)session.getAttribute("userInfo");
		if(userInfo == null)
		{
			response.sendRedirect(request.getContextPath()+"/noSession");//跳转到超时界面
			return false;
		}
		
		//权限验证
		HandlerMethod hm = (HandlerMethod)handler;
		Class theClass = hm.getBeanType(); //访问的类
		Method theMethod = hm.getMethod(); //访问的方法
		Access acc_class = (Access) theClass.getAnnotation(Access.class); //类注释
		Access acc_method = theMethod.getAnnotation(Access.class); //方法注释
		
		if(authMap == null)
		{
			authMap =  roleService.getAuthMap();
		}
	
		if(acc_class!=null)
		{//类权限验证
			String authCode = acc_class.authCode();
			if(!xx.isEmpty(authCode))
			{
				if(!this.canAccess(userInfo, authCode))
				{
					this.write(request, response);
					return false;
				}
			}
		}
		
		if(acc_method!=null)
		{//方法权限验证
			String authCode = acc_method.authCode();
			if(!xx.isEmpty(authCode))
			{
				if(!this.canAccess(userInfo, authCode))
				{
					this.write(request, response);
		            return false;
				}
			}
		}
		
		return true;
	}

	
	/**
	 * 回写信息
	 */
	private void write(HttpServletRequest request, HttpServletResponse response) throws IOException
	{
		if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))
		{ //ajax请求，则返回错误消息 
			Message msg = new Message();
			msg.setCode(Message._ERROR);
			msg.setMsg("无相关仅限，操作失败！！！");
			Gson gson = xx.getGson();
			
			response.setCharacterEncoding("UTF-8");
		    response.setContentType("application/json;charset=UTF-8");
			PrintWriter out = response.getWriter();  
		    out.print(gson.toJsonTree(msg));//消息
		    
		    out.flush();
		}else{//非ajax请求，则直接跳转
			response.sendRedirect(request.getContextPath()+"/noAccess");
		}
	}
	
	/**
	 * 验证用户是否具有某项权限
	 */
	private boolean canAccess(UserInfo userInfo,String authCode)
	{
		List<Long> roleIds = userInfo.getRoleIds(); //用户拥有的角色ID列表
		if(roleIds == null)
		{//用户没有拥有任何角色
			return false;
		}
		String ids = authMap.get(authCode);
		if(ids == null)
		{//资源没有分配给任何角色
			return false;
		}
		for(Long id:roleIds)
		{
			if(ids.indexOf(","+id.longValue()+",") >= 0)
			{
				return true;
			}
		}
		return false;
	}

	/**
	 * Handler执行之后，ModelAndView返回之前调用这个方法
	 */
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
	{
	}

	/**
	 * Handler执行完成之后调用这个方法
	 */
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exc) throws Exception
	{
	}

	public static void setAuthMap(Map<String, String> authMap)
	{
		AuthInterceptor.authMap = authMap;
	}

}
